package crystal.workshop.ui;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Collection;

import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import crystal.workshop.StructureSource;

public class SimpleStructureList implements StructuresListModel {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 2942307935331489192L;

	protected Hashtable<StructureDataModel, StructureSource> structures =
		new Hashtable<StructureDataModel, StructureSource>();
	
	protected EventListenerList listenerList = new EventListenerList();
	
	protected transient PropertyChangeSupport propertyChange;
	
	/**
	 * Accessor for the propertyChange field.
	 */
	protected PropertyChangeSupport getPropertyChange() {
		if (propertyChange == null) {
			propertyChange = new PropertyChangeSupport(this);
		};
		return propertyChange;
	}
	
	@Override
	public Collection<StructureDataModel> getStructures() {
		return structures.keySet();
	}

	@Override
	public void updateStructure(StructureDataModel str) {
		final StructureSource src = structures.get(str);
		if(src != null) {
			src.update(str);
		}
	}

	@Override
	public void addPropertyChangeListener(final PropertyChangeListener listener) {
		getPropertyChange().addPropertyChangeListener(listener);
	}

	@Override
	public void removePropertyChangeListener(final PropertyChangeListener listener) {
		getPropertyChange().removePropertyChangeListener(listener);
	}
	
	@Override
	public void addListDataListener(final ListDataListener l) {
		listenerList.add(ListDataListener.class, l);
	}
	
	@Override
	public void removeListDataListener(final ListDataListener l) {
		listenerList.remove(ListDataListener.class, l);
	}

	@Override
	public void add(final StructureDataModel str, final StructureSource source) {
		final int index = structures.size();
		structures.put(str, source);
		fireIntervalAdded(index, index);
	}

	@Override
	public void addAll(final Collection<? extends StructureDataModel> strs,
			final StructureSource source) {
		final int index = structures.size();
		for( StructureDataModel str : strs ) {
			structures.put(str, source);
		}
		fireIntervalAdded(index, index + strs.size() - 1);
	}
	
	@Override
	public void remove(final StructureDataModel str) {
		// TODO: Have to get the correct index!
		final int index = structures.size();
		fireIntervalRemoved(index, index);
		firePropertyChange("structures", Arrays.asList(new StructureDataModel[]{str}), null);
	}
	
	@Override
	public int getSize() {
		return structures.size();
	}
	
	@Override
	public StructureDataModel getStructure(final int index) {
		StructureDataModel str = null;
		final Enumeration<StructureDataModel> strs = structures.keys();
		int i;
		// Try moving the enumeration to the correct position
		for(i = 0; i < index && strs.hasMoreElements(); strs.nextElement(), ++i) {}
		if(i == index && strs.hasMoreElements()) str = strs.nextElement();
		return str;
	}
	
	/**
	 * File a property changed event
	 */
	public void firePropertyChange(final String propertyName, java.lang.Object oldValue, java.lang.Object newValue) {
		getPropertyChange().firePropertyChange(propertyName, oldValue, newValue);
	}
	
	public StructureDataModel find(final String name) {
		for(StructureDataModel str : structures.keySet()) {
			if(str.getCustomName().equals(name)) {
				return str;
			}
		}
		return null;
	}
	
    /**
     * Called when one or more elements are added to the model.  The new
     * elements are specified by a closed interval index0, index1 -- the
     * enpoints are included.  Note that index0 need not be less than or equal
     * to index1.
     * 
     * @param index0 one end of the new interval
     * @param index1 the other end of the new interval
     * @see EventListenerList
     */
    protected void fireIntervalAdded(int index0, int index1)
    {
    	ListDataListener[] listeners = (ListDataListener[])listenerList.getListeners(ListDataListener.class);;
    	ListDataEvent e = null;
    	
		for(int i = listeners.length - 1; i >= 0; i -= 1) {
			if(e == null) {
			    e = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, index0, index1);
			}
			listeners[i].intervalAdded(e);	       
		}
    }
    
    /**
     * Called when one or more elements are removed from the model.  The old
     * elements are specified by a closed interval index0, index1 -- the
     * enpoints are included.  Note that index0 need not be less than or equal
     * to index1.
     * 
     * @param index0 one end of the new interval
     * @param index1 the other end of the new interval
     * @see EventListenerList
     */
    protected void fireIntervalRemoved(int index0, int index1)
    {
    	ListDataListener[] listeners = (ListDataListener[])listenerList.getListeners(ListDataListener.class);;
    	ListDataEvent e = null;
    	
		for(int i = listeners.length - 1; i >= 0; i -= 1) {
			if(e == null) {
			    e = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, index0, index1);
			}
			listeners[i].intervalRemoved(e);	       
		}
    }
}
